package com.finalministry.server.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.finalministry.database.connection.FinalministryDatabaseConnection;
import org.finalministry.database.dao.DaoFactory;
import org.finalministry.objects.osm.OSM_Bounds;
import org.finalministry.objects.osm.OSM_Node;
import org.finalministry.objects.osm.OSM_Tag;
import org.finalministry.objects.osm.OSM_Way;
import org.finalministry.objects.osm.OSM_Way_Node_Ref;
import org.finalministry.utils.geo.GeoUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.vividsolutions.jts.geom.Geometry;

/**
 * Imports a full OSM file into the H2 Database
 * 
 * @author lussopi
 */
public class OsmFullImporter {

	private static final int TENTHOUSAND = 10000;
	private static final int MILLION = 500000;
	private static String POLYGON_ARRAY[] = { "building", "area" };
	private static List<String> POLYGONs = Arrays.asList(POLYGON_ARRAY);

	private static String LINE_ARRAY[] = { "highway", "railway" };
	private static List<String> LINEs = Arrays.asList(LINE_ARRAY);

	public static void main(String[] args) {

		try {
			SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser saxParser = factory.newSAXParser();

			DaoFactory.setFinalMinistryConnection(new FinalministryDatabaseConnection("finalministryTest", "sa", "sa", 7786));

			DefaultHandler handler = new DefaultHandler() {

				long counter = 0;
				long thousand = TENTHOUSAND;
				long million = MILLION;
				String osmID_NODE = null;
				String osmID_WAY = null;
				String osmID_RELATION = null;
				String osmID_NODE_REF = null;

				OSM_Way way = null;
				List<OSM_Way_Node_Ref> wayNodeRef_LIST = new ArrayList<OSM_Way_Node_Ref>();
				List<OSM_Tag> tag_LIST = new ArrayList<OSM_Tag>();

				public void startElement(String uri, String localName, String element, Attributes attributes) throws SAXException {

					if ("bounds".equals(element)) {

						String minlat = attributes.getValue("minlat");
						String minlon = attributes.getValue("minlon");
						String maxlat = attributes.getValue("maxlat");
						String maxlon = attributes.getValue("maxlon");

						String polygonBounds = GeoUtils.getPolygonTextFromBounds(minlat, minlon, maxlat, maxlon);

						System.out.println(polygonBounds);
						try {
							Geometry geometry = GeoUtils.getNonSphericalFromSpherical(polygonBounds);
							OSM_Bounds bounds = new OSM_Bounds(null, new Long(1), geometry);
							DaoFactory.getInstance().getOsmDAO().createOSM_Bounds(bounds);
						} catch (Exception e1) {
							e1.printStackTrace();
						}

						return;
					}

					if ("node".equals(element)) {

						String lat = attributes.getValue("lat");
						String lon = attributes.getValue("lon");
						osmID_NODE = attributes.getValue("id");

						try {
							String pointAsText = "POINT (" + lat + " " + lon + ")";
							Geometry geometry = GeoUtils.getNonSphericalFromSpherical(pointAsText);
							OSM_Node node = new OSM_Node(null, new Long(osmID_NODE), new Long(1), geometry);
							DaoFactory.getInstance().getOsmDAO().createOSM_Node(node);
						} catch (Exception e) {
							e.printStackTrace();
						}

						thousand = thousand - 1;
						counter += 1;

						if (thousand < 0) {
							System.out.println(counter);
							thousand = TENTHOUSAND;
						}
					}

					if ("way".equals(element)) {

						osmID_WAY = attributes.getValue("id");

						try {

							if (osmID_WAY != null) {
								Long wayID = new Long(osmID_WAY);

								way = new OSM_Way(null, wayID, new Long(1), null);
								// ... this will be persisted at the end of the
								// element
							}
						} catch (Exception e) {
							e.printStackTrace();
						}

						return;
					}

					if ("nd".equals(element)) {

						osmID_NODE_REF = attributes.getValue("ref");

						try {

							if (osmID_NODE_REF != null) {
								Long nodeRefID = new Long(osmID_NODE_REF);

								OSM_Way_Node_Ref wayNodeRef = new OSM_Way_Node_Ref(null, new Long(osmID_WAY), nodeRefID);
								// DaoFactory.getInstance().getOsmDAO().createOSM_WayNodeRef(wayNodeRef);

								wayNodeRef_LIST.add(wayNodeRef);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}

						return;
					}

					if ("tag".equals(element)) {

						String key = attributes.getValue("k");
						String value = attributes.getValue("v");

						try {
							Long nodeID = null;
							Long wayID = null;
							Long relationID = null;

							if (osmID_NODE != null) {
								nodeID = new Long(osmID_NODE);
							} else if (osmID_WAY != null) {
								wayID = new Long(osmID_WAY);
							} else if (osmID_RELATION != null) {
								relationID = new Long(osmID_RELATION);
							}

							if (nodeID != null || wayID != null || relationID != null) {

								OSM_Tag tag = new OSM_Tag(null, new Long(1), wayID, nodeID, relationID, key, value);
								tag_LIST.add(tag);
								DaoFactory.getInstance().getOsmDAO().createOSM_Tag(tag);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}

						return;
					}

					thousand = thousand - 1;
					million = million - 1;
					counter += 1;

					if (thousand < 0) {
						if (way == null) {
							System.out.println(counter);
						} else {
							System.out.println(counter + " ways");
						}
						thousand = TENTHOUSAND;
					}

					if (million < 0) {

						System.err.println("-------------------------------------");
						million = MILLION;
					}

				}

				public void endElement(String uri, String localName, String element) throws SAXException {

					if ("node".equals(element)) {
						osmID_NODE = null;
						tag_LIST.clear();
					}

					if ("way".equals(element)) {

						if (tagsContains(POLYGONs)) {
							try {
								String spatialData = OsmFullImporter.enrichWays(wayNodeRef_LIST, true);
								DaoFactory.getInstance().getOsmDAO().createOSM_Way(way, spatialData);
							} catch (Exception e) {
								e.printStackTrace();
							}
						} else if (tagsContains(LINEs)) {
							try {
								String spatialData = OsmFullImporter.enrichWays(wayNodeRef_LIST, false);
								DaoFactory.getInstance().getOsmDAO().createOSM_Way(way, spatialData);
							} catch (Exception e) {
								e.printStackTrace();
							}
						}

						osmID_WAY = null;
						wayNodeRef_LIST.clear();
						tag_LIST.clear();
					}

					if ("relation".equals(element)) {
						osmID_RELATION = null;
						tag_LIST.clear();
					}
				}

				private boolean tagsContains(List<String> tagList) {

					for (OSM_Tag tag : tag_LIST) {
						if (tagList.contains(tag.getKey())) {
							return true;
						}
					}
					return false;
				}

				public void characters(char ch[], int start, int length) throws SAXException {

				}

			};

			saxParser.parse(args[0], handler);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static String enrichWays(List<OSM_Way_Node_Ref> wayNodeRef_LIST, boolean isPolygon) throws Exception {

		String beginning = "POLYGON ((";
		String end = "))";

		if (isPolygon == false) {
			beginning = "LINESTRING (";
			end = ")";
		}

		StringBuffer polygon = new StringBuffer(beginning);
		String firstPoint = null;

		boolean first = true;

		for (OSM_Way_Node_Ref wayNodeRef : wayNodeRef_LIST) {
			OSM_Node node = DaoFactory.getInstance().getOsmDAO().findNodeByOsmID(wayNodeRef.getNodeID());

			if (!first) {
				polygon.append(",");
			}

			String point = node.getGeometry().toText().replace("POINT (", " ").replace(")", " ").trim();
			polygon.append(point);

			first = false;
		}

		polygon.append(end);

		System.out.println(polygon.toString());

		return polygon.toString();
	}
}
